package db

import (
	"errors"
	"fmt"
	"gorm.io/driver/mysql"
	"gorm.io/gorm"
	"log"
	"time"
)

type Users struct {
	gorm.Model
	id         uint   `gorm:"primaryKey"`
	Username   string `gorm:"unique"`
	Password   []byte `gorm:"not null"`
	CreateDate int64
	UpdateDate int64
	LastHostID uint
	GroupIDs   string
	Remask     string
	AllGroups  uint
}

type Hosts struct {
	gorm.Model
	Id         uint   `gorm:"primaryKey"`
	Addr       string `gorm:"not null"`
	Hostname   string `gorm:"not null"`
	CreateDate int64
	UpdateDate int64
	Password   []byte
	Key        []byte
	Remask     string
}

type Groups struct {
	gorm.Model
	Id         uint   `gorm:"primaryKey"`
	Names      string `gorm:"unique"`
	HostsIDs   string
	Remask     string
	CreateDate int64
	UpdateDate int64
}

var db *gorm.DB
var err error

func init() {
	log.SetPrefix("db: ")
	log.SetFlags(log.Ldate | log.Ltime | log.Lshortfile)
}

func InitDB(address string) {
	//db, err = gorm.Open(sqlite.Open("goAgentServer.db"), &gorm.Config{})
	db, err = gorm.Open(mysql.Open(address), &gorm.Config{
		//ConnPool: gorm.PoolConfig{},
	})
	if err != nil {
		log.Panicln(err)
	}
	//db = db.Debug()
	err = db.AutoMigrate(&Users{})
	if err != nil {
		log.Panicln(err)
	}
	err = db.AutoMigrate(&Groups{})
	if err != nil {
		log.Panicln(err)
	}
	err = db.AutoMigrate(&Hosts{})
	if err != nil {
		log.Panicln(err)
	}

}

func UpdateUserLastHostID(username interface{}, hostid uint) {
	var user Users
	db.Where("username = ?", username).Find(&user)
	user.LastHostID = hostid
	db.Save(user)
}

func QueryUserInfo(username interface{}) Users {
	var user Users
	db.Where("username = ?", username).Find(&user)

	return user
}

func QueryUserAllInfo() []Users {
	var user []Users
	db.Find(&user)

	return user
}

func QueryHostsAllInfo() []Hosts {
	var hosts []Hosts
	db.Find(&hosts)

	return hosts
}

func QueryGroupsAllInfo() []Groups {
	var groups []Groups
	db.Find(&groups)

	return groups
}

func QueryHostsInfo(hostID interface{}) (Hosts, error) {
	var hosts Hosts
	err := db.First(&hosts, "id = ?", hostID).Error
	if err != nil {
		return hosts, errors.New("find hosts erorrs ")
	}
	return hosts, nil
}

func QueryGroupInfo(groupID interface{}) (Groups, error) {
	var group Groups
	err := db.First(&group, "id = ?", groupID).Error
	if err != nil {
		return group, errors.New("find groups errors ")
	}

	//log.Println("dbs ", group)
	return group, nil
}

func ManageGroups(name string, hostids string, remask string, delgroupid string) error {

	var group Groups

	if name == "" {
		//del
		if db.Where("id = ?", delgroupid).Delete(&group).Error != nil {
			log.Println("not fount groups info , del errors")

			return errors.New(fmt.Sprintf("groups del erorr , no fount groups ,groupId: %v", delgroupid))
		}
		return nil
	} else {
		err = db.Where("names = ?", name).First(&group).Error

		if err != nil {
			//add
			log.Println("infos: ", name, hostids, remask)
			err = db.Create(&Groups{
				Names:      name,
				HostsIDs:   hostids,
				Remask:     remask,
				CreateDate: time.Now().Unix(),
				UpdateDate: time.Now().Unix(),
			}).Error
			if err != nil {
				return errors.New(fmt.Sprintf("groups add error : %v", err))
			}
		} else {
			//update
			group.HostsIDs = hostids
			group.Remask = remask
			group.UpdateDate = time.Now().Unix()

			if db.Save(&group).Error != nil {
				return errors.New(fmt.Sprintf("groups update error: %v", err))
			}
			return nil
		}
	}

	return nil
}

func ManageHosts(addr string, username string, password []byte, key []byte, remask string, delhostid int, updatehostid int) error {

	var host Hosts

	// add
	if -1 == delhostid && -1 == updatehostid {
		if db.Create(&Hosts{
			Addr:       addr,
			Hostname:   username,
			Password:   password,
			Key:        key,
			Remask:     remask,
			CreateDate: time.Now().Unix(),
			UpdateDate: time.Now().Unix(),
		}).Error != nil {
			log.Println("host add error ", err)
			return errors.New("host add error")
		}
	}

	// update
	if -1 == delhostid && -1 != updatehostid {
		err = db.First(&host, "id = ?", updatehostid).Error
		if err != nil {
			if err.Error() == "record not found" {
				log.Println("host record not found.")
				return errors.New("host record not found.")
			}
		}

		if username != "" {
			host.Hostname = username
		}

		if len(password) == 0 {
			host.Password = password
		}

		if len(key) == 0 {
			host.Key = key
		}

		if addr != "" {
			host.Addr = addr
		}

		if remask != "" {
			host.Remask = remask
		}

		host.UpdateDate = time.Now().Unix()

		if db.Save(&host).Error != nil {
			log.Println("host save error ", err)
			return errors.New(fmt.Sprintf("host save error , ids: %v", updatehostid))
		}
	}

	// del
	if -1 != delhostid && -1 == updatehostid {
		if db.Where("id = ?", delhostid).Delete(&host).Error != nil {
			return errors.New(fmt.Sprintf("users host error , ids: %v", delhostid))
		}
		return nil
	}
	return nil
}

func ManageUsers(username string, passwd []byte, groups string, remask string, userId int) (error, bool) {

	var user Users

	if -1 == userId {
		if username == "" {
			return errors.New("username is none"), false
		}

		err := db.First(&user, "username = ?", username).Error
		if err != nil {
			if err.Error() == "record not found" {
				log.Println("users record not found ,now add it.")

				if db.Create(&Users{
					Username:   username,
					Password:   passwd,
					GroupIDs:   groups,
					Remask:     remask,
					CreateDate: time.Now().Unix(),
					UpdateDate: time.Now().Unix(),
					AllGroups:  0,
				}).Error != nil {
					return errors.New(fmt.Sprintf("users add record error %v", err)), false
				}
				return nil, true
			} else {
				return errors.New(fmt.Sprintf("users record fount error : %v", err)), false
			}
		}

		user.GroupIDs = groups
		user.UpdateDate = time.Now().Unix()
		user.Remask = remask
		if db.Save(&user).Error != nil {
			return errors.New(fmt.Sprintf("users update recode error :%v", err)), false
		}
		return nil, false
	} else if userId > 0 {

		if db.Unscoped().Where("id = ?", userId).Delete(&user).Error != nil {
			return errors.New(fmt.Sprintf("users delete error , ids: %v", userId)), false
		}
		return nil, false
	}
	return errors.New(fmt.Sprintf("users update error , error ids: %v", userId)), false
}
